use std::{collections::HashMap, fs::read_to_string};

use regex::Regex;

struct Kanji(HashMap<u32, String>);

/// convert from kanji to pinyin
impl Kanji {
    fn build() -> Self {
        let mut map = HashMap::new();
        let reg = Regex::new(r"U\+(.*?): (.*?)[, ]").unwrap();
        for l in read_to_string("../../topic1/solutiont4/kanji.txt")
            .unwrap()
            .lines()
        {
            let (_, [unicode, pinyin]) = reg.captures(l).unwrap().extract();
            let unicode = u32::from_str_radix(unicode, 16).unwrap();
            map.insert(unicode, pinyin.to_owned());
        }
        Self(map)
    }
    fn transfer(&self, ch: char) -> String {
        let unicode = ch as u32;
        let res = self.0.get(&unicode);
        if let Some(s) = res {
            s.to_owned()
        } else {
            String::new()
        }
    }
}

/// cancel the tone of pinyin
struct Oto {
    otoed: Vec<Vec<char>>,
    pured: Vec<char>,
}
impl Oto {
    fn build() -> Self {
        let mut otoed = vec![];
        let mut pured = vec![];
        for (i, l) in read_to_string("../../topic1/solutiont4/oto.txt")
            .unwrap()
            .lines()
            .enumerate()
        {
            if i < 4 {
                otoed.push(l.chars().collect());
            } else {
                pured = l.chars().collect();
            }
        }
        Self { otoed, pured }
    }
    fn transfer(&self, ch: char) -> char {
        for v in &self.otoed {
            for (i, &c) in v.iter().enumerate() {
                if c == ch {
                    return self.pured[i];
                }
            }
        }
        ch
    }
}

/// convert from pinyin to wei-pinyin
struct Wei(HashMap<String, String>);
impl Wei {
    fn build() -> Self {
        let mut map = HashMap::new();
        for l in read_to_string("../../topic1/solutiont4/wei.txt")
            .unwrap()
            .lines()
        {
            let mut it = l.split(' ');
            let wei = it.next().unwrap().to_owned();
            let _ = it.next();
            let pinyin = it.next().unwrap().to_owned();
            map.insert(pinyin, wei);
        }
        Self(map)
    }
    fn transfer(&self, py: &str) -> String {
        let res = self.0.get(py);
        if let Some(str) = res {
            str.to_owned()
        } else {
            py.to_owned()
        }
    }
}

pub fn converter(input: &str) -> String {
    let kanji = Kanji::build();
    let oto = Oto::build();
    let wei = Wei::build();
    let ans = input
        .chars()
        .map(|x| kanji.transfer(x))
        .map(|s| s.chars().map(|c| oto.transfer(c)).collect::<String>())
        .map(|x| wei.transfer(&x))
        .collect::<Vec<_>>()
        .join(" ");
    up(ans)
}

/// let the first character uppercase
fn up(s: String) -> String {
    let mut c = s.chars();
    match c.next() {
        Some(a) => a.to_uppercase().chain(c).collect(),
        None => String::new(),
    }
}
